package com.meixun.xuanke.handle.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.meixun.xuanke.handle.dao.CourseDao;
import com.meixun.xuanke.handle.model.Course;
import com.meixun.xuanke.handle.model.MqCourseInfo;
import com.meixun.xuanke.handle.service.ActiveMQService;
import com.meixun.xuanke.handle.service.CourseService;
import com.meixun.xuanke.handle.util.CourseUtil;
import com.meixun.xuanke.handle.util.RedisLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Service
public class CourseServiceImpl implements CourseService {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private CourseDao courseDao;
    @Resource
    ActiveMQService activeMQService;

    @Override
    public void handleCourse(MqCourseInfo mqCourseInfo) {

        if(mqCourseInfo.getState()==MqCourseInfo.state_add){
            //选课
            addCourse(mqCourseInfo);
        }else if(mqCourseInfo.getState()==MqCourseInfo.state_remove){
            //退课
            removeCourse(mqCourseInfo);

        }else {
            logger.info("其他操作");
        }
    }

    @Override
    public void addStuCourseRecord(MqCourseInfo mqCourseInfo) {

        //查询课程名称
        Course course=courseDao.getCourseName(mqCourseInfo.getCourseid());
        //插入选课操作日志
        String text=mqCourseInfo.getStudentid()+"选择课程"+course.getCoursename();
        int code=101;//选课
        if(mqCourseInfo.getState()==MqCourseInfo.state_remove){
            text=mqCourseInfo.getStudentid()+"退出课程"+course.getCoursename();
            code=102;
        }
        courseDao.addStuCourseRecord(mqCourseInfo.getSchoolid(),mqCourseInfo.getTermid(),mqCourseInfo.getCourseid(),code,text,new Date(),mqCourseInfo.getInputer());
    }

    private void removeCourse(MqCourseInfo mqCourseInfo){
        String result="200";
        String key ="removeCourse:"+mqCourseInfo.getCourseid()+":"+mqCourseInfo.getStudentid();
        HashMap<String,Object> map = courseDao.getStuCourse(mqCourseInfo.getCourseid(),mqCourseInfo.getStudentid(),mqCourseInfo.getTermid());
        if (map!=null){
            if(!"0".equals(map.get("state")+"")){
                //修改选课状态
                courseDao.updateStuCourse(0,new Date(),mqCourseInfo.getInputer(),map.get("id")+"");
                //更新数据
                //移除自己课程里面的数据
                String keyIds="studentCourseIds:"+mqCourseInfo.getStudentid();
                redisTemplate.opsForSet().remove(keyIds,mqCourseInfo.getCourseid().toString());
                //删除key
                String keyMyList="studentCourseList:"+mqCourseInfo.getStudentid();
                redisTemplate.delete(keyMyList);
                //更新课程人数
                String keySize="courseSize:"+mqCourseInfo.getCourseid();
                redisTemplate.opsForValue().set(keySize,courseDao.getCourseSize(mqCourseInfo.getCourseid()));

                //异步添加操作日志
                activeMQService.addStuCourseRecord(mqCourseInfo);
            }else{
                result="您已退课或者未选择，请勿重复操作（107）";
            }
            //更新缓存

        }else{
            result="退课失败，未查询到您的当前课程的选课记录（106）";
        }
        logger.info(result);
        //返回结果
        redisTemplate.opsForValue().set(key,result,5,TimeUnit.MINUTES);
    }

    private void addCourse(MqCourseInfo mqCourseInfo){
        String result="选课失败:服务器繁忙";
        String key ="addCourse:"+mqCourseInfo.getCourseid()+":"+mqCourseInfo.getStudentid();
        JSONObject configJson= getConfig(mqCourseInfo.getSchoolid(),mqCourseInfo.getTermid());
        boolean isSuecss=true;
        //是否有往期关联
        if (configJson.getIntValue("isSetRelation")==1){
            List<HashMap<String, Object>> mycList = courseDao.queryMyCourseByCouId(mqCourseInfo.getCourseid(), mqCourseInfo.getStudentid());
            if(mycList.size()>0){
                result="不能和往期选择同门科目， 请选择其他课程（112）";
                isSuecss=false;
            }
        }
        Course course = courseDao.getCourseById(mqCourseInfo.getCourseid());
        if(isSuecss){
            long size=courseDao.getCourseSize(mqCourseInfo.getCourseid());
            if(size>=course.getMaxsize()){
                result="当前课程人数已满，请选择其他课程（107）";
                isSuecss=false;
            }
        }
        if(isSuecss){
            if(course.getType()==0){
                result="选课失败，当前课程不支持学生操作，请选择其他课程（105）";
                isSuecss=false;
            }
        }
        if(!isSuecss){
            //返回结果 加个过期时间
            redisTemplate.opsForValue().set(key,result,5,TimeUnit.MINUTES);
            return;
        }
        //先处理常规逻辑
        RedisLock lock = new RedisLock(redisTemplate, mqCourseInfo.getCourseid().toString(), 10000, 20000);
        try {
            if (lock.lock()) {
                //选课逻辑
                int multiCourse=configJson.getIntValue("multiCourse");
                List<Course> myList =courseDao.getMyCourseList(mqCourseInfo.getStudentid(),mqCourseInfo.getSchoolid(),mqCourseInfo.getTermid());
                if (multiCourse!=0&&myList.size()>=multiCourse){
                    result="超过选课数量[ "+multiCourse+" ]（110）";
                    isSuecss=false;
                }
                if(isSuecss){
                    for (int i = 0; i <myList.size(); i++) {
                        Course course1=myList.get(i);
                        if(course1==null||course1.getId()==null){
                            continue;
                        }
                        //是否和我的选课时间冲突
                        if (configJson.getIntValue("isCheckTime")==1){
                            if(configJson.getIntValue("timeType")==0){
                                if (CourseUtil.isTimeConflict( course1.getTimeText(),course.getTimeText()+"")){
                                    isSuecss=false;
                                    result="选课失败，当前课程和["+course1.getCoursename()+"]课程时间冲突";
                                    break;
                                }
                            }else if(configJson.getIntValue("timeType")==1){
                                if (CourseUtil.isTimeConflict( course1.getTimeJson(),course.getTimeJson()+"")){
                                    isSuecss=false;
                                    result="选课失败，当前课程和["+course1.getCoursename()+"]课程时间冲突";
                                    break;
                                }
                            }
                        }
                    }

                }

                if(!isSuecss){
                    //返回结果 加个过期时间
                    redisTemplate.opsForValue().set(key,result,5,TimeUnit.MINUTES);
                    return;
                }
                //开始选课
                int version = courseDao.queryCourseVersion(mqCourseInfo.getCourseid());
                //保存选课记录
                HashMap<String,Object> map = courseDao.queryStuCourse(mqCourseInfo.getCourseid(),mqCourseInfo.getStudentid(),mqCourseInfo.getTermid());
                if (map==null){
                    //新增选课记录
                    courseDao.addStuCourse(mqCourseInfo.getSchoolid(),mqCourseInfo.getStudentid(),mqCourseInfo.getTermid(),mqCourseInfo.getCourseid(),1,new Date(),mqCourseInfo.getInputer());
                }else{
                    //修改选课记录
                    courseDao.updateStuCourse(1,new Date(),mqCourseInfo.getInputer(),map.get("id")+"");
                }
                //修改版本并重复校验数量
                int rst= courseDao.updateCourseVersion(mqCourseInfo.getCourseid(),version);
                if (rst==0){
                    //修改失败,手动回滚
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                    result="选课失败,选择当前课程人数较多,服务器繁忙，请稍后重试（113）";
                }else {
                    //更新数据
                    result="200";
                    //添加自己课程里面的数据
                    String keyIds="studentCourseIds:"+mqCourseInfo.getStudentid();
                    redisTemplate.opsForSet().add(keyIds,mqCourseInfo.getCourseid().toString());

                    //删除key，清空自己的选课列表，自己获取。
                    String keyMyList="studentCourseList:"+mqCourseInfo.getStudentid();
                    redisTemplate.delete(keyMyList);

                    //更新课程人数
                    String keySize="courseSize:"+mqCourseInfo.getCourseid();
                    redisTemplate.opsForValue().set(keySize,courseDao.getCourseSize(mqCourseInfo.getCourseid()));

                    //异步添加操作日志
                    activeMQService.addStuCourseRecord(mqCourseInfo);
                }

            }
        } catch (InterruptedException e) {
            result="选课失败,选择当前课程人数较多,服务器繁忙，请稍后重试（115）"+e.getMessage();
            e.printStackTrace();
        } finally {
            // 为了让分布式锁的算法更稳键些，持有锁的客户端在解锁之前应该再检查一次自己的锁是否已经超时，再去做DEL操作，因为可能客户端因为某个耗时的操作而挂起，
            // 操作完的时候锁因为超时已经被别人获得，这时就不必解锁了。 ————这里没有做
            lock.unlock();
        }
        //返回结果 加个过期时间
        redisTemplate.opsForValue().set(key,result,5,TimeUnit.MINUTES);

    }

    public JSONObject getConfig(String schoolId, Integer termTypeid) {

        String  key = "config:getConfig_schoolId_" + schoolId+"_termTypeid_"+termTypeid;
        boolean hasKey = redisTemplate.hasKey(key);
        if (hasKey) {
            JSONObject object=JSONObject.parseObject(redisTemplate.opsForValue().get(key).toString());
            return object;
        }
        HashMap<String, Object> object =courseDao.getConfig(schoolId);
        if(object==null){
            return null;
        }
        try {
            JSONObject  configJson=JSONObject.parseObject(object.get("configJson")+"");
            if(termTypeid!=null){
                HashMap<String, Object> termTypeObject =courseDao.getXTermType(termTypeid);
                try {
                    JSONObject  termTypeConfigJson=JSONObject.parseObject(termTypeObject.get("configJson")+"");
                    if(termTypeConfigJson!=null){
                        Set<String> keySet= termTypeConfigJson.keySet();
                        for (String jsonkey : keySet) {
                            configJson.put(jsonkey,termTypeConfigJson.get(jsonkey));
                        }
                        object.put("configJson",configJson);
                    }
                }catch (Exception e){ }
            }
            redisTemplate.opsForValue().set(key, configJson.toJSONString(), 600, TimeUnit.SECONDS);
            return configJson;
        }catch (Exception e){
            return null;
        }

    }
}
